#pragma once
#include <string>
#include <unordered_map>

class Soundex
{
    static const size_t MaxCodeLength{ 4 };

public:
    std::string encode(const std::string& word) const {
        return zeroPad(head(word) + encodeDigits(tail(word)));
    }

private:
    std::string head(const std::string& word) const {
        return word.substr(0, 1);
    }

    std::string tail(const std::string& word) const {
        return word.substr(1);
    }

    std::string encodeDigits(const std::string& word) const {
        std::string encoding;
        for (auto letter : word)
        {
            if (isComplete(encoding))
                break;
            
            encoding += encodeDigit(letter);
        }
        return encoding;
    }

    bool isComplete(const std::string& encoding) const {
        return encoding.length() == MaxCodeLength - 1;
    }

    std::string encodeDigit(char letter) const {
        const std::unordered_map<char, std::string> encodings{
            {'b', "1"}, {'f', "1"}, {'p', "1"}, {'v', "1"},
            {'c', "2"}, {'g', "2"}, {'j', "2"}, {'k', "2"}, {'q', "2"},
                        {'s', "2"}, {'x', "2"}, {'z', "2"},
            {'d', "3"}, {'t', "3"},
            {'l', "4"},
            {'m', "5"},
            {'r', "6"}
        };

        auto finder = encodings.find(letter);
        return finder == encodings.end() ? "" : finder->second;
    }

    std::string zeroPad(const std::string& word) const {
        auto zeroNeeded = MaxCodeLength - word.length();
        return word + std::string(zeroNeeded, '0');
    }
};